<template>
  <mxp-popup
    ref="popupRef"
    model="bottom"
    :background="options.background || defThemeColor.fill"
    :offsetBottom="options.offsetBottom"
    :toolbarOptions="options.toolbarOptions"
    :showToolbar="options.showToolbar"
    :zIndex="options.zIndex"
    :maskBackground="options.maskBackground"
    :maskClose="options.maskClose"
    @hide="(e) => emits('hide', e)"
  >
    <view style="position: relative; width: 750rpx">
      <view v-if="data.loadState" class="loading-box" :style="{ background: getAmendAlphaColor(options.background, 0.8) }">
        <slot name="loading">
          <mxp-icon name="jiazai" :spin="true" :color="loadingIconColor || defThemeColor.border" size="30px"></mxp-icon>
        </slot>
      </view>
      <slot name="header"></slot>
      <view v-if="data.showPicker" key="view-2" class="picker-view-box-vessel">
        <picker-view
          class="picker-view"
          :indicatorStyle="`height: ${options.itemHeight};line-height: ${options.itemHeight};`"
          :value="data.defaultValueIndex"
          :style="{ height: `calc(${options.itemHeight} * ${options.showItemCount})`, width: '750rpx' }"
          @change="bindChange"
          @pickstart="pickstart"
          @pickend="pickend"
        >
          <picker-view-column v-for="(arr, index1) in data.dateList" :key="index1">
            <view v-for="(item, index2) in arr" :key="index2" class="picker-item" :style="{ height: options.itemHeight }">
              <text
                :style="{
                  fontSize: index2 === data.defaultValueIndex[index1] ? options.activeFontSize : options.fontSize,
                  color: index2 === data.defaultValueIndex[index1] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                }"
              >
                {{ item.label }}
              </text>
            </view>
          </picker-view-column>
        </picker-view>
      </view>
    </view>
    <slot name="bottom"></slot>
  </mxp-popup>
</template>

<script lang="ts" setup>
  import MxpPopup from '../mxp-popup/mxp-popup.vue';
  import type { $ToolbarProps } from '../mxp-toolbar/mxp-toolbar.vue';
  import { reactive, ref } from 'vue';
  import MxpIcon from '../mxp-icon/mxp-icon.vue';
  import type { $AllColorStr, $SizeUnitStr } from '../mxp-tool/types/interface';
  import { isObject } from '../mxp-tool/utils/verify';
  import { getAmendAlphaColor } from '../mxp-tool/utils/color';
  import { removeObjectEmpty } from '../mxp-tool/utils/obj';
  import type { PickerViewOnChangeEvent } from 'mxp-uni-types';
  import { defThemeColor } from '../mxp-tool/theme/theme';

  export type $PickerProps = {
    /** 数组 需要展示的数据 可以是一维数组和二维数组 */
    list?: unknown[]; // eslint-disable-line vue/no-unused-properties
    /** 当为对象数组时,需要展示的 key,不传则取 valueKey,如果 lvalueKey 也没传则为 'label' */
    labelKey?: number | string; // eslint-disable-line vue/no-unused-properties
    /** 当为对象数组时.需要返回的值的 key，不传则取 labelKey,如果 labelKey 也没传则为 'value' */
    valueKey?: number | string; // eslint-disable-line vue/no-unused-properties
    /** 层级，最多支持五级 */
    level?: 1 | 2 | 3 | 4 | 5; // eslint-disable-line vue/no-unused-properties
    /** 背景颜色 */
    background?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 文本的颜色 */
    color?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的文本颜色 */
    activeColor?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 默认文字的大小 */
    fontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的默认文字的大小 */
    activeFontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 偏移量 （如自定义底部的tabbar就需要一个偏移量） */
    offsetBottom?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 默认选中的数据 */
    defaultValue?: any; // eslint-disable-line vue/no-unused-properties
    /** 是否留出安全区域 */
    safeSrea?: boolean; // eslint-disable-line vue/no-unused-properties
    /** zIndex */
    zIndex?: number; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    title?: string; // eslint-disable-line vue/no-unused-properties
    /** 是否显示顶部工具栏 */
    showToolbar?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 顶部工具栏配置项 */
    toolbarOptions?: $ToolbarProps; // eslint-disable-line vue/no-unused-properties
    /** 每列显示多少排 */
    showItemCount?: number; // eslint-disable-line vue/no-unused-properties
    /** 单个选项的高度 */
    itemHeight?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 遮罩的背景色 */
    maskBackground?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 加载状态图标的颜色 */
    loadingIconColor?: $AllColorStr;
    /** 点击确认按钮，是否自动关闭  */
    autoClose?: boolean; // eslint-disable-line vue/no-unused-properties
  };
  export type $ListItem = { label: string; value: unknown; index: number; item: any };
  export type $PickerConfirmResult = { valueList: any[]; indexList: number[]; itemList: any[] };
  export type $PickerChangeResult = { selected: $PickerConfirmResult; levelIndex: number; index: number; label: any; value: any; item: any };
  export type $CallBack = {
    confirm?: (data: $PickerConfirmResult) => void;
    change?: (data: $PickerChangeResult) => void;
  };
  export type $Options = $CallBack & $PickerProps;

  defineOptions({
    name: 'mxp-picker',
  });
  const props = withDefaults(defineProps<$PickerProps>(), {
    background: undefined as $AllColorStr | undefined,
    offsetBottom: '0px',
    showSecond: false,
    level: undefined as 1 | 2 | 3 | 4 | 5 | undefined,
    safeSrea: true,
    zIndex: 888,
    title: '请选择',
    toolbarOptions: () => ({}),
    list: () => [],
    labelKey: undefined as number | string | undefined,
    valueKey: undefined as number | string | undefined,
    defaultValue: [],
    showToolbar: true,
    color: undefined as $AllColorStr | undefined,
    activeColor: undefined as $AllColorStr | undefined,
    fontSize: '14px',
    activeFontSize: '16px',
    showItemCount: 7,
    itemHeight: '44px',
    maskBackground: undefined as $AllColorStr | undefined,
    maskClose: undefined as boolean | undefined,
    loadingIconColor: undefined as $AllColorStr | undefined,
    autoClose: true,
  });
  defineSlots<{
    /** 自定义加载中展示 */
    loading: () => void;
    /** 自定义顶部内容 */
    header: () => void;
    /** 自定义底部内容 */
    bottom: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'hide', type: 'button' | 'confirm' | 'mask' | 'rest'): void;
  }>();
  const popupRef = ref<InstanceType<typeof MxpPopup> | null>(null);
  const options = ref<$CallBack & Required<$PickerProps>>(JSON.parse(JSON.stringify(props)));
  const data = reactive({
    loadState: false,
    showPicker: false,
    isSubmit: true,
    dateList: [] as $ListItem[][],
    defaultValueIndex: [0, 0, 0, 0, 0, 0],
  });
  /** 显示 */
  function show(param: $Options = {}) {
    data.loadState = false;
    data.isSubmit = true;
    initData(param);
    popupRef.value?.show({
      confirm() {
        if (data.isSubmit) {
          typeof options.value.confirm === 'function' && options.value.confirm(getSubmitDataFun());
          options.value.autoClose && hide();
        }
      },
    });
    data.showPicker = false;
    setTimeout(() => {
      data.showPicker = true;
    }, 10);
  }

  /**
   * 隐藏
   */
  function hide() {
    popupRef.value?.hide();
  }

  /** 初始化数据 */
  function initData(param: $Options = {}) {
    const op: $Options = { ...props, ...removeObjectEmpty(param) };
    const _labelKey = op.labelKey || op.valueKey;
    const _valueKey = op.valueKey || op.labelKey;
    // 防止为 0 时被默认为 'label' 或者 'value'
    op.labelKey = _labelKey === 0 ? 0 : _labelKey || 'label';
    op.valueKey = _valueKey === 0 ? 0 : _valueKey || 'value';
    if (!op.toolbarOptions) {
      op.toolbarOptions = { title: op.title };
    } else {
      op.toolbarOptions!.title = op.title;
    }
    // 把 list 转换为  unknown[][]
    let list: unknown[][] = [];
    if (op.list!.length > 0 && Array.isArray(op.list![0])) {
      list = op.list as unknown[][];
    } else {
      list = [op.list!];
    }

    // 处理层级 如果层级大于5则默认为5
    let level = op?.level || list.length;
    level = level > 5 ? 5 : level < 1 ? 1 : level;

    if (level < list.length) {
      // 如果二维数组list 的长度大于op.level 则截取
      list.length = level;
    }
    op.list = list as unknown[];

    // 把 list 转换为  { label: string; value: any; item: any }[][] 并且赋值给 data.dateList
    data.dateList = list.map((arr) => {
      const newArr: $ListItem[] = arr.map((item, index) => {
        if (isObject(item)) {
          return { label: String(item[op.labelKey!] || ''), value: item[op.valueKey!], item, index };
        } else {
          return { label: String(item), value: item, item, index };
        }
      });
      return newArr;
    });
    op.defaultValue = Array.isArray(op.defaultValue) ? op.defaultValue : [op.defaultValue];
    data.defaultValueIndex = getIndexByValue(op.defaultValue, data.dateList);
    options.value = op as $CallBack & Required<$PickerProps>;
  }

  /** 滚轮改变 */
  function bindChange(e: PickerViewOnChangeEvent) {
    const val = e.detail.value;
    // 得到是那一列的滚轮发生变化的索引值
    const levelIndex = val.findIndex((item, i) => item !== (data.defaultValueIndex[i] || 0));
    const changeIndex = val[levelIndex]!;
    data.defaultValueIndex = val;
    const submitData = getSubmitDataFun();
    const item = data.dateList[levelIndex]![changeIndex]!;
    const res: $PickerChangeResult = { selected: submitData, levelIndex, index: changeIndex, label: item.label, value: item.value, item: item.item };
    typeof options.value.change === 'function' && options.value.change(res);
  }

  /** 通过默认值拿到下标 */
  function getIndexByValue(value: unknown[], list: $ListItem[][]) {
    const indexList: number[] = [];
    const vLen = value.length;
    list.forEach((item, i) => {
      if (vLen >= i) {
        const index = item.findIndex((_item) => _item.value === value[i]);
        indexList.push(index >= 0 ? index : 0);
      } else {
        indexList.push(0);
      }
    });
    return indexList;
  }

  /**
   * 得到最终需要返回的数据
   */
  function getSubmitDataFun(): $PickerConfirmResult {
    const valueList: any[] = [];
    const indexList: number[] = [];
    const itemList: any[] = [];
    data.defaultValueIndex.forEach((item, index) => {
      const obj: $ListItem = data.dateList[index]![item]!;
      valueList.push(obj.value);
      indexList.push(obj.index);
      itemList.push(obj.item);
    });
    return { valueList, indexList, itemList };
  }

  /** 设置加载状态 */
  function setLoadState(state: boolean) {
    data.loadState = Boolean(state);
  }

  /**
   * 更新列表
   */
  function undateList(list: unknown[][], defaultValue: any[]) {
    // 把 list 转换为  { label: string; value: any; item: any }[][] 并且赋值给 data.dateList
    data.dateList = list.map((arr) => {
      const newArr: $ListItem[] = arr.map((item, index) => {
        if (isObject(item)) {
          return { label: String(item[options.value.labelKey!] || ''), value: item[options.value.valueKey!], item, index };
        } else {
          return { label: String(item), value: item, item, index };
        }
      });
      return newArr;
    });
    options.value.defaultValue = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
    data.defaultValueIndex = getIndexByValue(defaultValue, data.dateList);
  }

  /** 滚动开始 */
  function pickstart() {
    data.isSubmit = false;
  }
  /** 滚动结束 */
  function pickend() {
    data.isSubmit = true;
  }

  defineExpose({
    /** 显示 */
    show,
    /** 隐藏 */
    hide,
    /** 提交 */
    confirm,
    /** 更新数据 */
    undateList,
    /** 设置加载状态 */
    setLoadState,
  });
</script>

<style lang="scss" scoped>
  .loading-box {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    z-index: 9999;
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    justify-content: center;
    height: 100%;
  }

  .picker-view-box-vessel {
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    justify-content: space-between;
    width: 750rpx;

    .picker-view {
      width: 750rpx;
      height: 200px;

      .picker-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        height: 40px;
        font-size: 13px;
        line-height: 40px;
        text-align: center;
      }
    }
  }
</style>
